package com.testing.sunnycommon.security.jwt;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * @Classname JwtUtils
 * @Description 类型说明
 * @Date 2023/6/30 22:13
 * @Created by 特斯汀Roy
 */
@Component
@Slf4j
public class JwtUtils {
    //用于加签的密钥，可以按自己的需求设置
    public final static String SECRET = "testingroy";

    //设置超时时间为24小时
    public final static Long EXPIRATION_TIME = 1000 * 60 * 60 * 24L;

    //设置携带token的头域为Authorization
    public final static String HEADER_STRING = "Authorization";

    //token前缀为Bearer
    public final static String TOKEN_PREFIX = "Bearer ";

    //token黑名单，在黑名单中的token为失效token
    public final static Set<String> blackList = new HashSet<>();


    /**
     * 生成令牌的方法
     *
     * @param userDetails 用户信息类型
     * @return 返回token值，其中拼接了Bearer作为前缀
     */
    public String generateToken(UserDetails userDetails) {

        //token中的信息声明
//        Map<String, Object> claims = new HashMap<>();
//        claims.put("sub", userDetails.getUsername());
//        claims.put("created", new Date());
        return Jwts.builder()
                //设置用户名作为主体
                .setSubject(userDetails.getUsername())
                //设置创建时间
                .setIssuedAt(new Date())
                //设置超时时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                //使用密钥加签
                .signWith(SignatureAlgorithm.HS512, SECRET)
                //生成token
                .compact();
    }

    /**
     * 从token中获取用户名
     *
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        String username = "";
        try {
            username = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token)
                    //获取声明体
                    .getBody()
                    //取出其中的subject也就是用户名。
                    .getSubject();
        } catch (Exception e) {
            log.error("解析token失败", e.fillInStackTrace());
        }
        return username;
    }

    /**
     * 验证令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            //获取token中的超时时间
            Date expiration = Jwts.parser().parseClaimsJws(token).getBody().getExpiration();
            //返回超时时间是否在当前时间之前
            return expiration.before(new Date());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 验证token是否和当前查到的用户是匹配的，并且没有过期，同时还需要这个token没有被放到失效黑名单里。
     *
     * @param token       令牌
     * @param userDetails 用户
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (!isTokenBlackListed(token) && username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

    /**
     * 将token加到黑名单里面，也就完成了失效的操作，但是失效的逻辑，是需要判断token在黑名单中。
     *
     * @param token
     */
    public void invalidateToken(String token) {
        blackList.add(token.substring(TOKEN_PREFIX.length()));
    }

    /**
     * 判断token在黑名单里
     *
     * @param token
     * @return
     */
    public Boolean isTokenBlackListed(String token) {
        return blackList.contains(token);
    }

}
